# Load libraries

library(tidyverse)
library(dplyr)
library(stringr)
library(lubridate)
library(sf)
library(leaflet)
library(leafpop)
library(htmltools)
library(scales)
library(highcharter)
library(reactable)
varnames <- c('Company',
              'Developer',
              'Application...Job..',
              'PV..kWAC.',
              'Metering..NA...NM...RNM...CDG.',
              'Application.Approved.Date...Utility.',
              'Project.Complete..Y.N.',
              'Zip.Code')

varreplace <- c('utility', 
                'developer', 
                'id',
                'capacity',
                'metering',
                'date',
                'complete',
                'zip')

ny <-   do.call(rbind,
          lapply(list.files(path = "raw data/NY/solar"), read.csv)) %>%
  select(varnames) %>% 
  rename_at(vars(varnames), ~varreplace) %>% 
  mutate(date = dmy(date),
         capacity = as.numeric(capacity),
         quarter = zoo::as.yearqtr(date, format = "%Y-%m-%d"),
         utility = case_when(
           utility == 'CECONY' ~ 'ConEd',
           utility == 'NIMO' ~ 'Niagara Mohawk (NatGrid)',
           TRUE ~ utility),
         metering = as.factor(case_when(
           is.na(metering) | metering == 'n/a' ~ 'Non-Metered',
           metering == 'NM' ~ 'Net Metered',
           metering == 'RNM' ~ 'Remote Net Metered',
           metering == 'FIT' ~ 'Feed-In Tariff',
           metering == 'CDG' ~ 'Community Solar',
           TRUE ~ 'Other')),
         segment_x = case_when(
           metering == 'Community Solar' ~ 'Community Solar',
           capacity <= 20 ~ 'Residential',
           capacity > 20 ~ 'Non-Residential'
           )) %>% 
  filter(!is.na(capacity))

ny_quarter <- ny %>% 
  filter(date >= '2020-07-01',
         quarter != '2021 Q3') %>% 
  group_by(segment_x, quarter) %>% 
  summarise(cap = sum(capacity)/1000)

ny_seg <- ny %>% 
  filter(quarter == '2021 Q2') %>% 
  mutate(res_cap = if_else(segment_x == 'Residential', capacity, 0),
         com_cap = if_else(segment_x == 'Non-Residential', capacity, 0),
         cs_cap = if_else(segment_x == 'Community Solar', capacity, 0),
        # month = months(as.Date(date)))
        month = month(date))

New York added round(sum(ny_seg$capacity)/1000,2) MW of new distributed solar capacity to the interconnection queue in Q2 2021

ny_quarter %>%
  hchart('column', hcaes(x = as.factor(quarter), y = round(cap,2), group = segment_x)) %>% 
  hc_colors(c('#F37325', '#2490BD', "#47B970", '#1A2B40', '#b7b7b7', '#800000','#F8AA1A')) %>% 
  hc_plotOptions(series = list(stacking = 'normal')) %>% 
  hc_xAxis(title = '', labels = list(step = 1) #, minorTickInterval
  ) %>% 
  hc_yAxis(title = list(text = 'Quarterly capacity (MW)'), style = list(fontSize = "5.0vh"))

Of this round(sum(ny_seg$capacity/1000),2) MW of new capacity…

ny_meter <- ny %>% 
  group_by(metering) %>% 
  summarise(cap = sum(capacity)) 
  

for (k in levels(ny_meter$metering)) {
  cat("- ", "**", round(ny_meter$cap[ny_meter$metering == k]/1000,2), "**", " MW was ", k, sep = "")
  cat("\n")
}
  • 5718.41 MW was Community Solar
  • 91.21 MW was Feed-In Tariff
  • 1442.11 MW was Net Metered
  • 52.94 MW was Non-Metered
  • 4.07 MW was Other
  • 461.93 MW was Remote Net Metered
ny %>% 
  group_by(metering) %>% 
  summarise(cap = sum(capacity)) %>% 
  hchart('pie', hcaes(x = metering, y = round(cap/1000,2)), name = "Capacity (MW)") %>% 
  hc_colors(c( '#2490BD', '#F37325', "#47B970", '#1A2B40', '#b7b7b7', '#F8AA1A', '#800000'))

Where was new capacity added in Q2 2021?

ny_map <- ny %>% 
  mutate(geoid2 = as.numeric(substr(zip,1,5))) %>% 
  filter(!geoid2 == 16807|!geoid2 == 17047) %>% 
  group_by(geoid2) %>% 
  summarise(cap = sum(capacity)/1000)

nymap <- st_read('raw data/NY/acs2019_5yr_B01003_86000US14475.shp') %>% 
  mutate(geoid2 = as.numeric(substr(geoid,8,12))) %>% 
  filter(!name == 'New York') %>% 
  left_join(ny_map, by = 'geoid2')

zip <- read.csv('raw data/uszips.csv') %>% 
  select(zip, city) %>% 
  rename('geoid2' = 'zip')

ny_int <- left_join(nymap, zip, by = 'geoid2') %>% 
  mutate(cap = round(cap*1000,0),
         cap2 = if_else(is.na(cap),0,cap))

map_pal <- c('#e0f1f9', '#2490BD') 

pal <- colorNumeric(palette = map_pal, domain = ny_int$cap, na.color = '#FFFFFF')
labels <- sprintf("<strong>%s</strong><br/> Zip code: %s<br/> Capacity Q2 2021: %s kW",
                  ny_int$city, paste(0, ny_int$geoid2, sep = ""), comma(ny_int$cap2)) %>% 
  lapply(HTML)

map_int <- leaflet(ny_int) %>% addTiles()
map_int %>% addPolygons(fillColor = ~pal(cap),
                        weight = 0.5,
                        opacity = 1,
                        color = "gray",
                        dashArray = "3",
                        fillOpacity = 0.7,
                        highlightOptions = highlightOptions(color = '#2a2a2a',
                                                            weight = 2,
                                                            dashArray = "",
                                                            fillOpacity = 0.7,
                                                            bringToFront = T),
                        label = labels,
                        labelOptions = labelOptions(
                          style = list("font-weight" = "normal", padding = "3px 8px"),
                          textsize = "15px",
                          direction = "auto",
                          opacity = 0.75)) %>% 
  addProviderTiles('Esri.WorldGrayCanvas')

Which utilities added the most capacity in Q2 2021?

ny %>%
  group_by(utility) %>% 
  summarise(cap = sum(capacity)) %>% 
  hchart('pie', hcaes(x = utility, y = cap), name = "Capacity (MW)") %>% 
  hc_colors(c('#F37325', '#2490BD', "#47B970", '#b7b7b7', '#1A2B40', '#800000','#F8AA1A')) 
ny %>%
  filter(quarter == '2021 Q2') %>% 
  mutate(month = factor(month(date), levels = c(4, 5, 6), labels = c('April', 'May', 'June'))) %>% 
  group_by(utility, month) %>% 
  summarise(cap = sum(capacity)) %>% 
  hchart('column', hcaes(x = month, y = cap, group = utility)) %>% 
  hc_colors(c('#F37325', '#2490BD', "#47B970", '#b7b7b7', '#1A2B40', '#800000','#F8AA1A')) %>% 
  hc_plotOptions(series = list(stacking = 'normal')) %>% 
  hc_xAxis(title = '', labels = list(step = 1) #, minorTickInterval
  ) %>% 
  hc_yAxis(title = list(text = 'Monthly capacity (MW)'), style = list(fontSize = "5.0vh"))
## `summarise()` has grouped output by 'utility'. You can override using the `.groups` argument.

Which developers added the most capacity in Q2 2021?

ny <- ny %>% 
  mutate(dev_agg = case_when(
    grepl('tesla|solarcity|solar city', developer, ignore.case = T) ~ 'Tesla/SolarCity',
    grepl('momentum', developer, ignore.case = T) ~ 'Momentum Solar',
    grepl('vivint|sunrun', developer, ignore.case = T) ~ 'Vivint/SunRun',
    grepl('sunpower|sun power', developer, ignore.case = T) ~ 'SunPower',
    grepl('venture', developer, ignore.case = T) ~ 'Venture Solar',
    grepl('trinity', developer, ignore.case = T) ~ 'Trinity Solar',
    grepl('empire', developer, ignore.case = T) ~ 'Empire Solar Solutions',
    grepl('plug', developer, ignore.case = T) ~ 'PlugPV',
    grepl('kasselman', developer, ignore.case = T) ~ 'Kasselman Solar',
    grepl('new york state solar|ny state solar', developer, ignore.case = T) ~ 'New York State Solar Farm',
    grepl('suncommon', developer, ignore.case = T) ~ 'SunCommon',
    grepl('infinity', developer, ignore.case = T) ~ 'Infinity Solar',
    grepl('borrego', developer, ignore.case = T) ~ 'Borrego Solar',
    grepl('resunance', developer, ignore.case = T) ~ 'RESunance',
    grepl('atlas', developer, ignore.case = T) ~ 'Atlas Renewables (CAERO Group)',
    grepl('amp solar d|amp solar u', developer, ignore.case = T) ~ 'Amp Solar',
    grepl('nexamp', developer, ignore.case = T) ~ 'Nexamp',
    grepl('renesola', developer, ignore.case = T) ~ 'ReneSolar Power Holdings',
    grepl('g&s', developer, ignore.case = T) ~ 'G&S Solar',
    grepl('delaware', developer, ignore.case = T) ~ 'Delaware River Solar',
    grepl('arcadia', developer, ignore.case = T) ~ 'Arcadia',
    grepl('oya', developer, ignore.case = T) ~ 'OYA Solar',
    grepl('citizens', developer, ignore.case = T) ~ 'Citizens Energy',
    grepl('long island', developer, ignore.case = T) ~ 'Long Island Power Solutions',
    grepl('kamtech|kam tech', developer, ignore.case = T) ~ 'Kamtech Solar',
    grepl('harvest', developer, ignore.case = T) ~ 'Harvest Power',
    grepl('empower', developer, ignore.case = T) ~ 'EmPower Solar',
    grepl('sunation', developer, ignore.case = T) ~ 'SUNation Solar',
    grepl('montante', developer, ignore.case = T) ~ 'Montante Solar',
    grepl('liberty', developer, ignore.case = T) ~ 'Solar Liberty Energy',
    grepl('lodestar', developer, ignore.case = T) ~ 'Lodestar Energy',
    grepl('omni', developer, ignore.case = T) ~ 'Omni-Navitas',
    grepl('bw ', developer, ignore.case = T) ~ 'BW Solar',
    grepl('aura', developer, ignore.case = T) ~ 'Aura Power',
    grepl('ric d|ric e', developer, ignore.case = T) ~ 'RIC Development',
    grepl('caero', developer, ignore.case = T) ~ 'CAERO Group',
    grepl('solitude', developer, ignore.case = T) ~ 'Solitude Solar',
    grepl('cypress c', developer, ignore.case = T) ~ 'Cypress Creek Renewables',
    grepl('saturn', developer, ignore.case = T) ~ 'Saturn Power Corporation',
    grepl('green l|greenl', developer, ignore.case = T) ~ 'Green Logic',
    grepl('hudson', developer, ignore.case = T) ~ 'Hudson River',
    grepl('microgrid', developer, ignore.case = T) ~ 'MicroGrid Networks',
    grepl('nextera|next era|dg new', developer, ignore.case = T) ~ 'NextEra Energy (DG New York CS)',
    grepl('eden', developer, ignore.case = T) ~ 'Eden Renewables',
    grepl('nexamp', developer, ignore.case = T) ~ 'Nexamp',
    grepl('source r', developer, ignore.case = T) ~ 'Source Renewables',
    grepl('CVE', developer) ~ 'CVE North America',
    grepl('convergent', developer) ~ 'Convergent Energy & Power',
    grepl('Nexus', developer) ~ 'Nexus Renewables',
    grepl('Dyna', developer) ~ 'DynaSolar DevCo',
    grepl('siemens', developer, ignore.case = T) ~ 'Siemens',
    grepl('Oriden', developer) ~ 'Oriden',
    grepl('sunrise', developer, ignore.case = T) ~ 'Sunrise Power Solutions',
    TRUE ~ developer    
  ))

ny_sum <- ny %>% 
  group_by(dev_agg, segment_x) %>% 
  summarise(cap = sum(capacity),
            n = n(),
            mean = mean(capacity)) %>% 
  ungroup() %>% 
  mutate(share = cap/sum(cap)) %>% 
  arrange(desc(cap))

Top 10 developers in the residential segment:

ny_sum %>% filter(segment_x == 'Residential') %>%
  head(10) %>%
  select(dev_agg, cap, n, share) %>% 
  mutate(share = paste(round(share*100,2), "%", sep = ""),
         cap = format(round(as.numeric(cap), 2), big.mark = ",")) %>% 
  knitr::kable(col.names = c("Developer", "Capacity (kW)", "Number of Projects", "Market Share"),
               digits = 2, align = 'l') %>% 
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = T #position = 'left'
                            ) %>% 
  kableExtra::add_header_above(c("Q2 2021" = 4))
Q2 2021
Developer Capacity (kW) Number of Projects Market Share
Vivint/SunRun 169,320.77 28127 2.18%
Tesla/SolarCity 127,442.03 19224 1.64%
Momentum Solar 46,435.80 8459 0.6%
SUNation Solar 43,878.91 5430 0.56%
Other 39,454.67 5764 0.51%
Trinity Solar 30,695.80 4872 0.4%
Long Island Power Solutions 23,945.22 3047 0.31%
EmPower Solar 23,927.05 2838 0.31%
Green Logic 22,062.18 2584 0.28%
Venture Solar 21,046.78 3555 0.27%

Top 10 developers in the commercial segment:

ny_sum %>% filter(segment_x == 'Non-Residential') %>%
  head(10) %>%
  select(dev_agg, cap, n, share) %>% 
  mutate(share = paste(round(share*100,2), "%", sep = ""),
         cap = format(round(as.numeric(cap), 2), big.mark = ",")) %>% 
  knitr::kable(col.names = c("Developer", "Capacity (kW)", "Number of Projects", "Market Share"),
               digits = 2, align = 'l') %>% 
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = T #position = 'left'
                            ) %>% 
  kableExtra::add_header_above(c("Q2 2021" = 4))
Q2 2021
Developer Capacity (kW) Number of Projects Market Share
Solar Liberty Energy 59,518.42 375 0.77%
Tesla/SolarCity 44,199.31 125 0.57%
Sunrise Power Solutions 42,061.71 187 0.54%
Borrego Solar 34,177.00 18 0.44%
Montante Solar 31,147.88 54 0.4%
Other 24,590.92 249 0.32%
Monolith Solar Assoc. LLC 23,790.00 312 0.31%
EnterSolar 22,896.98 38 0.29%
21,889.30 182 0.28%
Safari Energy 19,791.23 79 0.25%

Top 10 developers active in the community solar segment:

ny_sum %>% filter(segment_x == 'Community Solar') %>%
  head(10) %>%
  select(dev_agg, cap, n, share) %>% 
  mutate(share = paste(round(share*100,2), "%", sep = ""),
         cap = format(round(as.numeric(cap), 2), big.mark = ",")) %>% 
  knitr::kable(col.names = c("Developer", "Capacity (kW)", "Number of Projects", "Market Share"),
               digits = 2, align = 'l') %>% 
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = T #position = 'left'
                            ) %>% 
  kableExtra::add_header_above(c("Q2 2021" = 4))
Q2 2021
Developer Capacity (kW) Number of Projects Market Share
Borrego Solar 399,410.0 125 5.14%
Nexamp 377,789.4 97 4.86%
Delaware River Solar 259,849.0 76 3.34%
BW Solar 181,500.0 39 2.34%
NextEra Energy (DG New York CS) 181,454.0 38 2.34%
Solar Liberty Energy 172,188.1 53 2.22%
OYA Solar 169,330.0 37 2.18%
Omni-Navitas 164,155.0 35 2.11%
Cypress Creek Renewables 158,194.9 50 2.04%
RIC Development 149,424.8 36 1.92%

Filter data

ny_table <- ny %>% 
  select(dev_agg, capacity, date, utility, segment_x, zip) %>% 
  rename('Developer' = dev_agg,
         'Capacity' = capacity,
         'Date' = date,
         'Utility' = utility,
         'Segment' = segment_x,
         'Zip Code' = zip)
reactable(ny_table, filterable = TRUE,
          columns = list(Capacity = colDef(filterable = FALSE)),
          searchable = TRUE,
          showPageSizeOptions = TRUE,
          pageSizeOptions = c(5,10,20,50),
          defaultPageSize = 10)